package sharewithus.analyzer.dic;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import sharewithus.analyzer.Bigram;
import sharewithus.analyzer.POSInf;
import sharewithus.analyzer.PartOfSpeech;
import sharewithus.analyzer.config.Configuration;
import sharewithus.analyzer.utils.MatchRet;
import sharewithus.analyzer.utils.TSTNode;
import sharewithus.analyzer.utils.TernaryInverseTrie;

/**
 * 二元词典
 * @author terry
 *
 */
public class Dictionary {
	
	private static final String PATH_DIR_CORE = "/sharewithus/analyzer/dic/CoreDic.txt";
	private static final String PATH_BIGRAM_CORE = "/sharewithus/analyzer/dic/BigramDic.txt";
	
	private TernaryInverseTrie<DicWord> trie;//词树
	private Map<Bigram, Integer> bigramMap;// 二元连词概率表
	private static final Dictionary instance;
	
	private int lastWordId = 1;//最后加入词典的词ID
	private long totalFreq = 0;//总词频
	
	static{
		instance = new Dictionary();
	}
	
	public static Dictionary getInstance() {
		return instance;
	}
	
	private Dictionary(){
		trie = new TernaryInverseTrie<DicWord>();
		bigramMap = new HashMap<Bigram, Integer>(16, .95f);
		loadCoreDic();//加载核心词典
		loadExternalDic();//加载扩展词典
		
		loadBigramDic(PATH_BIGRAM_CORE);//加载核心二元词典
		//加载扩展二元词典
		for (String extBigramDicPath : Configuration.getExtBigramDictionarys()) {
			loadBigramDic(extBigramDicPath);
		}
	}
	
	
	/**
	 * 加载核心词典
	 */
	private void loadCoreDic(){
		String line, term, speecnName = null;
		PartOfSpeech pos;
		InputStream ins = Dictionary.class.getResourceAsStream(Dictionary.PATH_DIR_CORE);
		if (ins == null) {
			throw new RuntimeException("Core Dictionary not found!!!");
		}
		BufferedReader in;
		try {
			in = new BufferedReader(new InputStreamReader(ins, "UTF-8"));
			while ((line = in.readLine()) != null) {
				StringTokenizer st = new StringTokenizer(line, " ");
				term = st.nextToken();
				while (st.hasMoreTokens()) {
					speecnName = st.nextToken();
					try {
						pos = PartOfSpeech.valueOf(speecnName);
					} catch (java.lang.IllegalArgumentException e) {
						pos = PartOfSpeech.UNKNOW;
					}
					int frq = Integer.parseInt(st.nextToken()); // 词性频率(注:词作为这个词性出现的频率)
					addWord(term, 0, pos, frq);
				}
			}
		} catch (IOException e) {
			System.err.println("Base Dictionary loading exception.");
			e.printStackTrace();
		} finally {
			try {
				if (ins != null) {
					ins.close();
					ins = null;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 加载扩展词典
	 */
	private void loadExternalDic(){
		List<String> dics = Configuration.getExtDictionarys();
		PartOfSpeech pos;//词性
		int f;//词频
		String dicPath;
		for (String line : dics) {
			StringTokenizer st = new StringTokenizer(line, ":");
			try {
				pos =PartOfSpeech.valueOf(st.nextToken()); 
			}catch(java.lang.IllegalArgumentException e){
				pos = PartOfSpeech.UNKNOW;
			}
			f = Integer.parseInt(st.nextToken());
			dicPath = st.nextToken();
			loadExternalDic(dicPath, pos, f);
		}
	}
	
	/**
	 * 加载扩展词典,此方法用于加载行业专用词.
	 * 由于无法得知专用词的实际词频，所以加载时需要指定假定的词频
	 * @param dicPath 词库路径
	 * @param pos 词性
	 * @param w 词频
	 */
	private void loadExternalDic(String dicPath , PartOfSpeech pos ,int w){
		String line = null;
		long code = 0;
		InputStream ins = Dictionary.class.getResourceAsStream(dicPath);
		BufferedReader br;
		if (ins == null) {
			throw new RuntimeException("Dictionary not found!!!");
		}
		try {
			br = new BufferedReader(new InputStreamReader(ins, "UTF-8"));
			while ((line = br.readLine()) != null) {
				StringTokenizer st = new StringTokenizer(line, ":");
				String key = "";
				if (st.hasMoreTokens()) {
					key = st.nextToken();
				}
				if (st.hasMoreTokens()) {
					try {
						code = Long.parseLong(st.nextToken());
					} catch (NumberFormatException e) {
						e.printStackTrace();
						code = 0;
					}
				}
				addWord(key, code, pos, w);
			}
		} catch (IOException e) {
			System.out.println("Dictionary loading exception.");
		} catch (java.util.NoSuchElementException e) {
			System.out.println("format error:" + line);
		} finally {
			try {
				if (ins != null) {
					ins.close();
					ins = null;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 加载二元词典
	 * @param dicPath
	 */
	private void loadBigramDic(String dicPath){
		InputStream ins = Dictionary.class.getResourceAsStream(dicPath);
		BufferedReader br;
		if (ins == null) {
			throw new RuntimeException(dicPath + " Dictionary not found!!!");
		}
		String line = null;
		String wordline = null;
		String prefixKey = null;
		String suffixkey = null;
		int frq = 0; /* 记录单词的频率 */
		TSTNode<DicWord> prefixNode = null; /* 前缀节点 */
		TSTNode<DicWord> suffixNode = null; /* 后缀节点 */

		try {
			br = new BufferedReader(new InputStreamReader(ins, "UTF-8"));
			while ((line = br.readLine()) != null) {
				// System.out.println(line);
				StringTokenizer st = new StringTokenizer(line, ":");
				wordline = st.nextToken();

				/* 求得@之前的部分 */
				prefixKey = wordline.substring(0, wordline.indexOf("@"));
				/* 求得@之后的部分 */
				suffixkey = wordline.substring(wordline.indexOf("@") + 1, wordline.length());

				/* 寻找后缀节点 */
				suffixNode = trie.getNode(suffixkey);
				if ((suffixNode == null) || (suffixNode.getData() == null)) {
					continue;
				}

				/* 寻找前缀节点 */
				prefixNode = trie.getNode(prefixKey);
				if ((prefixNode == null) || (prefixNode.getData() == null)) {
					continue;
				}

				frq = Integer.parseInt(st.nextToken()); /* 记录二元连词的频率 */
				Bigram key = Bigram.getKey(prefixNode.getData().getId(), suffixNode.getData().getId());
				//System.out.println(key + ":" + frq);
				this.bigramMap.put(key, frq);

			}
		} catch (IOException e) {
			System.out.println(dicPath + " Dictionary loading exception.");
		} catch (java.util.NoSuchElementException e) {
			System.out.println("format error:" + line);
		} finally {
			try {
				if (ins != null) {
					ins.close();
					ins = null;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 方法描述：将词加入到词典中
	 * @param term 词文本
	 * @param code 语义编码
	 * @param pos 词性
	 * @param frq 频率
	 */
	private void addWord(String term, long code, PartOfSpeech pos, int frq) {
		if ("".equals(term))
			return;
		totalFreq += frq;
		DicWord word;
		TSTNode<DicWord> currentNode = trie.getOrCreateNode(term);
		// 新增节点
		if (currentNode.getData() == null) {
			word = new DicWord(lastWordId++, term);
			word.getPosInfs().add(new POSInf(pos, frq, code));
			currentNode.setData(word);
		} else { // 已有节点
			currentNode.getData().getPosInfs().add(new POSInf(pos, frq, code));
		}
	}
	
	/**
	 * 提取二元连词频率
	 * @param key 二元词key
	 * @return
	 */
	public int getBigramFreq(Bigram key){
		return bigramMap.get(key) == null ? 0 : bigramMap.get(key);
	}
	
	/**
	 * 
	 * @return 最末词id
	 */
	public int lastWordId() {
		return lastWordId;
	}

	/**
	 * 
	 * @return 总词频
	 */
	public long totalFreq() {
		return totalFreq;
	}
	
	/**
	 * 方法描述：从文本的指定位置上匹配出前驱词序列
	 * @param text 文本
	 * @param offset 位置
	 * @param ret 匹配结果序列
	 */
	public void matchAll(String text, int offset,List<MatchRet<DicWord>> ret) {
		trie.matchAll(text, offset, DicWord.getUnknowWord(), ret);
	}
	
	/**
	 * 方法描述：从文本的指定位置上匹配出最长词
	 * @param text 文本
	 * @param offset 位置
	 * @return
	 */
	public MatchRet<DicWord> matchLong(String text, int offset){
		return trie.matchLong(text, offset, DicWord.getUnknowWord());
	}
	
}
